package com.okami.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;

/**
 * @author: TanJingyu
 * @create: 2020-07-01 17:11
 **/
@EnableWebSecurity(debug = true)
//@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true) //开启方法权限注解支持
@EnableGlobalMethodSecurity(prePostEnabled = true) //开启方法权限注解支持
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Resource
    private UserDetailsService okamiUserDetailsService;
//    @Resource
//    private AuthenticationProvider okamiAuthenticationProvider;
//    @Autowired
//    private MetaDataAttributeConfig metaDataAttributeConfig;

//    @Autowired
//    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
//        //在内存中临时配置，练习使用
//        auth.inMemoryAuthentication()
//                .passwordEncoder(new BCryptPasswordEncoder())
//                .withUser("zhangfei").password(new BCryptPasswordEncoder().encode("123")).roles("role_admin");
//
//        //自定义service，自定义用户信息对象，结合数据库使用
//        auth.userDetailsService(new UserDetailService()).passwordEncoder(new BCryptPasswordEncoder());
//    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //在内存中临时配置，练习使用
//        auth.inMemoryAuthentication()
//                .passwordEncoder(new BCryptPasswordEncoder())
//                .withUser("zhangfei").password(new BCryptPasswordEncoder().encode("123")).roles("role_admin");
//        auth.inMemoryAuthentication()
//                .passwordEncoder(new BCryptPasswordEncoder())
//                .withUser("zhangfei").password(new BCryptPasswordEncoder().encode("123")).authorities("add","remove");

        // 自定义service，自定义用户信息对象，结合数据库使用
        auth.userDetailsService(okamiUserDetailsService).passwordEncoder(new BCryptPasswordEncoder());

        // 设置自定义认证流程，Provider
        // 设置自定义provider,就不用设置service了，在provider中设置service
//        auth.authenticationProvider(okamiAuthenticationProvider);
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
//                .antMatchers("/**").permitAll()
//                .anyRequest().authenticated()
                .anyRequest().authenticated();
                //配置访问路径对应权限规则（意思不使用在方法上添加注解，用数据库）
                // 先注释掉了，因为自定义的这个东西无论什么路径我都返回需要admin角色，会导致上面配置的permitAll失效
//                .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
//                    @Override
//                    public <O extends FilterSecurityInterceptor> O postProcess(O object) {
////                        object.setAccessDecisionManager(customUrlDecisionManager);
//                        object.setSecurityMetadataSource(metaDataAttributeConfig);
//                        return object;
//                    }
//                })
//                .and()
//                .formLogin();
        http.csrf().disable();
//        http.addFilterAfter(new OkamiFilter(), UsernamePasswordAuthenticationFilter.class);

        //🏚访问被拒绝的处理方法。
        //访问被拒绝分两种情况，一种是未登录：走authenticationEntryPoint。一种是登陆了但是无权限访问：走accessDeniedHandler
        http.exceptionHandling().accessDeniedHandler((HttpServletRequest req, HttpServletResponse res, AccessDeniedException e) -> {
            System.out.println("accessDeniedHandler...........");

            //设置响应内容的格式
            //文字
//            res.setContentType("text/plain;charset=utf-8");
            //fson串
            res.setContentType("application/json");
            res.setCharacterEncoding("utf-8");

//            ServletOutputStream outputStream = res.getOutputStream();
//            outputStream.write("没有权限，真是抱歉！！！！！".getBytes());
//            outputStream.flush();
//            outputStream.close();

            PrintWriter writer = res.getWriter();
            writer.write("没有权限。抱歉。。。。。。");
            writer.flush();
            writer.close();

        });

        //未登录，访问被拒绝，默认的authenticationEntryPoint逻辑为重定向到登陆页
//        http.exceptionHandling().authenticationEntryPoint((HttpServletRequest req, HttpServletResponse res, AuthenticationException e) -> {
//            System.out.println("AuthenticationEntryPoint.................");
//            System.out.println(e.getMessage());
//
//            res.setContentType("application/json;charset=utf-8");
//            PrintWriter writer = res.getWriter();
//            writer.write("请登录。。。。。。");
//            writer.flush();
//            writer.close();
//        });

    }
}
